﻿namespace EasyWeb.Validation
{
    using System;
    using System.ComponentModel.DataAnnotations;
    using System.Globalization;
    using System.Reflection;

    [AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
    public class EWPropertyCompareValidationAttribute : EWCompareValidationAttribute
    {
        public EWPropertyCompareValidationAttribute(string otherProperty, EWCompareMode mode)
            : base(mode)
        {
            EWGuard.IsNotNullOrEmpty(otherProperty, "otherProperty");

            this.OtherProperty = otherProperty;
        }

        public string OtherProperty { get; private set; }

        public string OtherPropertyDisplayName { get; private set; }

        public override bool RequiresValidationContext
        {
            get { return true; }
        }

        public override string FormatErrorMessage(string name)
        {
            return string.Format(CultureInfo.CurrentCulture, this.MessageTemplate, name, this.OtherPropertyDisplayName ?? this.OtherProperty);
        }

        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            var type = validationContext.ObjectType.GetTypeInfo();

            var otherProperty = type.GetProperty(this.OtherProperty, BindingFlags.FlattenHierarchy | BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
            if (otherProperty == null)
            {
                throw new InvalidOperationException(string.Format("The property {0} is not found in type {1}.", this.OtherProperty, type.FullName));
            }

            var otherValue = otherProperty.GetValue(validationContext.ObjectInstance, null);

            var compareResult = Compare(value, otherValue);

            if (!compareResult)
            {
                if (this.OtherPropertyDisplayName == null)
                {
                    this.OtherPropertyDisplayName = EWHelpers.DataAnnotation.GetDisplayNameForProperty(type, this.OtherProperty);
                }

                return new ValidationResult(this.FormatErrorMessage(validationContext.DisplayName));
            }

            return null;
        }
    }
}
